module I2C (
    input       wire                    clk,
    input       wire                    rst,
    output      reg     [7:0]           data,
    output      reg                     scl,
    inout       wire                    sda
);

reg                                     sda_sel;
reg                                     sda_buf;

assign sda = sda_sel ? sda_buf : 1'bz;

//clk 50M, scl 100K, 先做一个200KHz的时钟

reg                                     clk_200K;
reg                 [7:0]               cnt_clk_200K;
localparam                              T_clk_200K = 125; // 1s/200k/20ns=125

always @ (posedge clk or posedge rst) begin
    if (rst)
        cnt_clk_200K <= 0;
    else if (cnt_clk_200K == T_clk_200K - 1)
        cnt_clk_200K <= 0;
    else
        cnt_clk_200K <= cnt_clk_200K + 1;
end

always @ (posedge clk or posedge rst) begin
    if (rst)
        clk_200K <= 0;
    else if (cnt_clk_200K == T_clk_200K - 1)
        clk_200K <= ~clk_200K;
    else
        clk_200K <= clk_200K;
end

//产生scl时钟
always@(negedge clk_200K or posedge rst) begin
    if (rst)
        scl <= 0;
    else
        scl <= ~scl;
end

//使用状态机发送数据
//从pcf8591读数据

reg                 [3:0]               cstate;

localparam          s_free              = 0;
localparam          s_start             = 1;
localparam          s_devadd            = 2;
localparam          s_ackdevadd         = 3;
localparam          s_regadd            = 4;
localparam          s_ackregadd         = 5;
localparam          s_restart           = 6;
localparam          s_devadd2           = 7;
localparam          s_ackdevadd2        = 8;
localparam          s_s_data            = 9;
localparam          s_m_nack            = 10;
localparam          s_stop              = 11;

reg                 [3:0]   num         = 0;
reg                 [7:0]   devadd      = 0;
reg                 [7:0]   regadd      = 0;

//clk_200K时钟的1s计数器
reg                 [31:0]              cnt;
localparam                              T_clk_200K_1s = 200_000;

always @ (posedge clk_200K or posedge rst) begin
    if (rst)
        cnt <= 0;
    else if (cnt == T_clk_200K_1s - 1)
        cnt <= 0;
    else
        cnt <= cnt + 1;
end

always @ (posedge clk_200K or posedge rst) begin
    if (rst)
        cstate <= s_free;
    else
        case (cstate)
            s_free          : begin
                                if (cnt==0) //每一秒中去读一次数据
                                    cstate<=s_start;
                                else begin
                                    num<=0;
                                    sda_buf<=0;
                                    cstate<=s_free;
                                    sda_sel <= 0;
                                end
                            end
            s_start         : begin
                                if (scl && sda) begin //产生从高到底的变化
                                    sda_sel <= 1;
                                    sda_buf<=0;
                                    devadd<=8'b10010000; //w=0
                                    cstate<=s_devadd;
                                end
                                else
                                    cstate<=s_start;
                            end
            s_devadd        : begin
                                if (!scl)
                                    if (num<8) begin
                                        sda_buf<= devadd[7-num];
                                        num<=num+1;
                                    end
                                    else begin
                                        sda_sel<=0; // set free
                                        num<=0;
                                        cstate<=s_ackdevadd;
                                    end
                            end
            s_ackdevadd     : begin
                                if (scl && !sda) begin
                                    cstate<=s_regadd;
                                    regadd<=8'b0100_0000;// prepare data
                                end
                                else
                                    cstate<=s_ackdevadd;
                            end
            s_regadd        : begin
                                if (!scl)
                                    if (num<8) begin
                                        sda_sel <= 1;
                                        sda_buf<= regadd[7-num];
                                        num<=num+1;
                                    end
                                    else begin
                                        sda_sel<=0; // set free
                                        num<=0;
                                        cstate<=s_ackregadd;
                                    end
                            end
            s_ackregadd     : begin
                                if (scl && !sda) begin
                                    cstate<=s_restart; // PCF8591
                                    sda_sel<=1;
                                    sda_buf<=1;//先拉高，再在scl高电平期间拉低，才形成启动信号
                                end
                            end
            s_restart       : begin
                                if (scl) begin
                                    sda_sel <= 1;
                                    sda_buf <= 0;
                                    devadd <= 8'b10010001; //r =1
                                    cstate <= s_devadd2;
                                end
                                // else if(!sda && !scl) begin
                                //     sda_sel <= 1;
                                //     sda_buf <= 1;
                                // end
                            end
            s_devadd2       : begin
                                if (!scl)
                                    if (num < 8) begin
                                        sda_sel <= 1;
                                        sda_buf <= devadd[7-num];
                                        num <= num + 1;
                                    end
                                    else begin
                                        sda_sel <= 0; //set free
                                        num <= 0;
                                        cstate <= s_ackdevadd2;
                                    end
                            end
            s_ackdevadd2    : begin
                                if (scl && !sda)
                                    cstate <= s_s_data;
                                else
                                    cstate <= s_ackdevadd;
                            end
            s_s_data        : begin //继续接收数据
                                if (scl) begin
                                    data[7-num] <= sda;
                                    if (num < 7)
                                        num <= num + 1;
                                    else begin
                                        cstate <= s_m_nack;
                                        num <= 0;
                                    end
                                end
                            end
            s_m_nack        : begin
                                if (!scl) begin
                                    sda_sel <= 1;
                                    sda_buf <= 1; //发送nack
                                    cstate <= s_stop;
                                end
                            end
            s_stop          : begin
                                if (num == 0) begin
                                    sda_buf <= 0;
                                    num <= num + 1;
                                end
                                else if (num == 1) begin
                                    sda_buf <= 1;
                                    num <= 0;
                                    cstate <= s_free;
                                end
                            end
            default         : cstate <= s_free;
        endcase
end

endmodule